#pragma once
#include "logger.hpp"
#include <gflags/gflags.h>
#include <ev.h>
#include <iostream>
#include <functional>
#include <amqpcpp.h>
#include <amqpcpp/libev.h>
#include <openssl/ssl.h>
#include <openssl/opensslv.h>
namespace xchat
{
class MQClient
{
        
    public:
        using ptr = std::shared_ptr<MQClient>;
        using MessageCallback = std::function<void(const char*, size_t)>;
        MQClient(const std::string &user, const std::string &pwd, const std::string &host)
        {
            _loop = EV_DEFAULT;
            _handler = std::make_unique<AMQP::LibEvHandler>(_loop);
            //amqp://root:299274142@127.0.0.1:5672/
            std::string url = "amqp://" + user + ":" + pwd + "@" + host + "/";
            AMQP::Address address(url);
            _connection = std::make_unique<AMQP::TcpConnection>(_handler.get(), address);
            _channel = std::make_unique<AMQP::TcpChannel>(_connection.get());
            _loop_thread = std::thread([this](){
                ev_run(_loop, 0);
            });
        }
    
        ~MQClient()
        {
            ev_async_init(&_async_watcher, watcher_callback);
            ev_async_start(_loop, &_async_watcher);
            ev_async_send(_loop, &_async_watcher);

            if (_loop_thread.joinable())
                _loop_thread.join(); //如果创建线程后不调用 join()（也不调用 detach()），线程对象销毁时会触发程序异常
            _loop = nullptr;
            //ev_async_stop(_loop, &_async_watcher);
            //ev_loop_destroy(_loop);
        }
        void declareComponents(const std::string &exchange, const std::string &queue,
            const std::string &routing_key = "routing_key", AMQP::ExchangeType exchange_type = AMQP::ExchangeType::direct)
        {
            //声明交换机
            _channel->declareExchange(exchange, exchange_type)
            .onError([](const char* message){
                LOG_ERROR("声明交换机失败：{}", message);
                exit(0);
            })
            .onSuccess([exchange](){
                LOG_INFO("{}, 声明交换机成功", exchange);
            });
            //声明队列
            _channel->declareQueue(queue)
            .onError([](const char* message){
                LOG_ERROR("声明队列失败：{}", message);
                exit(0);
            })
            .onSuccess([queue](){
                LOG_INFO("{}, 声明队列成功", queue);
            });
            //针对交换机和队列进行 绑定
            _channel->bindQueue(exchange, queue, routing_key) //routing key 同名就行
            .onError([&](const char* message){
                LOG_ERROR("交换机和队列进行 绑定失败：{},{}, 原因 {}",exchange, queue, message);
                exit(0);
            })
            .onSuccess([&](){
                LOG_INFO("{}, {}, {}, 交换机和队列进行 绑定成功", exchange, queue, routing_key);
            });
        }
        bool publish(const std::string &exchange, const std::string &msg, 
                    const std::string &routing_key = "routing_key")
        {
            bool ret = _channel->publish(exchange, routing_key, msg);
            if(ret == false)
            {
                LOG_ERROR("{}, 消息发布失败", exchange);
                return false;
            }
            return true;
        }
        void consume(const std::string &queue, const MessageCallback &cb)
        {
            //auto Callback = std::bind(cb, _channel, std::placeholders::_1, std::placeholders::_2,  std::placeholders::_3);
            _channel->consume(queue, "consume-tag")
            //AMQP::TcpChannel *channel, const AMQP::Message &message, uint64_t deliveryTag, bool redelivered
            .onReceived([this, cb](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered)
            {
                cb(message.body(), message.bodySize());
                _channel->ack(deliveryTag);
            }) //返回值不一样
            .onError([](const char* message){
                LOG_ERROR("订阅队列信息 失败：{}", message);
                exit(0);
            });
        }
    private:
        static void watcher_callback(struct ev_loop *loop, ev_async *watcher, int32_t revents)
        {
            ev_break(loop, EVBREAK_ALL);
        }
    private:
        struct ev_async _async_watcher;
        struct ev_loop *_loop;          //即使类成员的声明顺序与初始化列表中的顺序一致，在 C++ 中也不建议在初始化列表中用前一个成员作为后一个成员的初始化参数。虽然语法上允许，且在多数情况下可能 “正常工作”，但存在潜在风险和规范问题  
        std::unique_ptr<AMQP::LibEvHandler> _handler;    //类没有无参构造 必须初始化列表
        std::unique_ptr<AMQP::TcpConnection> _connection;
        std::unique_ptr<AMQP::TcpChannel> _channel;
        std::thread _loop_thread;
};
}